﻿#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QScreen>

#include "myserial.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

#define DEBUG_ENABLE false

typedef enum
{
    UPDATE_IDEL = 0,
    UPDATE_SEND_FILEINFO,
    UPDATE_SEND_ERASE,
    UPDATE_SEND_FIRMWARE,
    UPDATE_DONE,
    UPDATE_ERROR

}eUpadteState_t;

typedef enum
{
    ERROR_NO = 0,
    ERROR_ACK,
    ERROR_CRC,
    ERROR_TIMEOUT,
    ERROR_UPDATE_FAILED

}eError_t;

class Widget : public QWidget
{
    Q_OBJECT

public:

    Widget(QWidget *parent = nullptr);
    ~Widget();

    /* 用户界面初始化 */
    void useruiInit();
    /* 串口的初始化 */
    void serialInit();
    /* 连接串口 */
    void connectSerial();
    /* 寻找固件 */
    void selectFirmware();
    /* 将hex文件转为bin文件 */
    QByteArray hexToBin(const QByteArray &fileData);
    /* 更新CRC校验和 */
    void updateCrcResult();
    /* 发送固件信息 */
    void sendFirmwareInfo();
    /* 发送擦除固件的指令 */
    void sendEraseCmd();
    /* 将数据分包 */
    QList<QByteArray> splitFirmware(const QByteArray &dat, int packetSize = 1024);
    /* 发送固件数据 */
    void sendFirmwareData();
    /* 升级固件 */
    void updateFirmware();


private slots:
    /* 按钮相关的信号槽 */
    void onBtnClickedSlots();
    /* 下拉款发生变化的信号槽 */
    void on_CbxCurrentIndexChangedSlots(int);

    /* 串口相关的槽 */
    void on_SerialSlost(const QString &event, const QString &SerialPortName = QString());
    /* 串口连接失败的信号槽 其实就是正在使用的串口被移除了 */
    void on_SerialConnectFaliSlost();
    /* 无应答信号槽 */
    void on_SerialNoAckSlost(quint8 funcCode);
    /* 有应答信号槽 */
    void on_SerialAckSlost(quint8 funcCode, const QByteArray &data);
    /* 校验码错误信号槽 */
    void on_ChechsumError(quint8 funcCode);

    /* 用定时器模拟的线程函数 */
    void on_ThreadFunction();
    /* 用于等待指令超时错误 */
    void on_TimeOutFunction();

public:
    /* 一个遮罩窗口 用于再升级过程中屏蔽点击事件 */
    QWidget *mask;

private:
    Ui::Widget *ui;
    /* 串口指针对象 */
    mySerial *serial;
    /* 用定时器模拟线程 */
    QTimer *threadTimer;
    /* 创建一个定时 用于超时等待 */
    QTimer *timeOutTimer;

    /* 用与存储文件内容 */
    QByteArray firmwareData;
    /* 用于存储文件类型 hex:0 bin:1 */
    quint8 firmwareType;
    /* 固件升级状态机的默认状态 */
    eUpadteState_t eUpateSte = UPDATE_IDEL;
    eError_t eErr = ERROR_NO;
    /* 用于存储固件的分包 */
    QList<QByteArray> firmwarePackets;
    /* 总包数 */
    quint32 totalPackets;
    /* 已发送的包数 */
    quint32 sentPackets;

    /* 数据包无响应 */
    quint8 sendFirmwareDataAck = true;

    /* 用于记录是擦除超时还是等待校验超时 超时说明校验失败 因为校验失败mcu不会发送应答的 */
    quint8 whichTimeStrike;
};
#endif // WIDGET_H
